package com.lhkj.ct.meta.modules.psychology.service;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.lhkj.ct.base.utils.AviatorUtil;
import com.lhkj.ct.base.utils.Validate;
import com.lhkj.ct.meta.modules.gauge.common.enums.GaugeType;
import com.lhkj.ct.meta.modules.gauge.mapper.ScaleDimensionMapper;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblScaleDimNorm;
import com.lhkj.ct.meta.modules.patient.mapper.PatientMapper;
import com.lhkj.ct.meta.modules.psychology.mapper.PsyRecordMapper;
import com.lhkj.ct.meta.modules.psychology.mapper.ScaleReportContentMapper;
import com.lhkj.ct.meta.modules.psychology.mapper.ScaleReportMapper;
import com.lhkj.ct.meta.modules.psychology.model.bo.AllotRecordBo;
import com.lhkj.ct.meta.modules.psychology.model.bo.DimensionBo;
import com.lhkj.ct.meta.modules.psychology.model.bo.SubmitGaugeBo;
import com.lhkj.ct.meta.modules.psychology.model.dto.ProcessDto;
import com.lhkj.ct.meta.modules.psychology.model.dto.ScaleSubmitDto;
import com.lhkj.ct.meta.modules.psychology.model.entity.TblGaugeAnswer;
import com.lhkj.ct.meta.modules.psychology.model.entity.TblScaleReport;
import com.lhkj.ct.meta.modules.psychology.model.entity.TblScaleReportContent;
import com.lhkj.ct.meta.modules.warning.service.ScaleWarnRecordService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * <p>
 * 测验提交
 * </p>
 */
@Service
public class PsyAnswerService {
    @Resource
    private PsyRecordMapper psyRecordMapper;

    @Resource
    private PatientMapper patientMapper;

    @Resource
    private ScaleReportMapper reportMapper;

    @Resource
    private ScaleReportContentMapper reportContentMapper;

    @Resource
    private ScaleDimensionMapper dimensionMapper;

    @Resource
    private GaugeAnswerService gaugeAnswerService;

    @Resource
    private ScaleWarnRecordService warnRecordService;

    @Resource
    private ScheduledExecutorService scheduledExecutorService;

    /**
     * 提交作答
     */
    @Transactional
    public void submit(ScaleSubmitDto scaleSubmitDto) {
        // 获取分配记录和报告id
        AllotRecordBo psyRecord = psyRecordMapper.selectRecordWithRid(scaleSubmitDto.getAllotId());
        Validate.isFalse(null == psyRecord, "分配记录不存在");
        // 2.获取档案信息
        SubmitGaugeBo.Patient patient = patientMapper.getSubmitUserInfo(psyRecord.getPatId());
        Validate.isFalse(null == patient, "档案不存在");
        Map<String, Object> params = scaleSubmitDto.getFormData();
        // 加入年龄，性别参数
        params.put("age", patient.getAge());
        params.put("spmAge", patient.getSpmAge());
        params.put("sex", patient.getSex());
        // 组装报告提交参数
        SubmitGaugeBo bo = new SubmitGaugeBo();
        bo.setFormData(params);
        bo.setAnswers(scaleSubmitDto.getAnswerRecords());
        bo.setGaugeId(psyRecord.getScaleId());
        bo.setPatient(patient);
        // 处理报告状态和提交时间
        reportMapper.update(null, new LambdaUpdateWrapper<TblScaleReport>()
                .set(TblScaleReport::getReportStatus, 0)
                .set(TblScaleReport::getSubmissionTime, LocalDateTime.now())
                .set(TblScaleReport::getAnswerJson, scaleSubmitDto.getAnswerJson())
                .eq(TblScaleReport::getId, psyRecord.getReportId()));
        // 处理得分、维度报告、预警记录
        this.afterSubmit(bo, psyRecord::getReportId, e -> gaugeAnswerService.submit(psyRecord.getReportId(), GaugeType.SCALE, e));
    }

    /**
     * 保存作答进度
     *
     * @param entity
     */
    public int saveProcess(ProcessDto entity) {
        return reportMapper.updateById(new TblScaleReport(){{
            setCostTime(entity.getCostTime());
            setAnswerJson(entity.getAnswerJson());
            setId(entity.getRId());
        }});
    }

    /**
     *
     * @param submitGaugeBo 量表id， 表单数据， 档案（姓名，性别，年龄）
     * @param getReportId   报告id
     * @param consumer      生成作答记录
     */
    @Transactional
    public void afterSubmit(SubmitGaugeBo submitGaugeBo, Supplier<String> getReportId, Consumer<List<TblGaugeAnswer>> consumer) {
        // 查询维度，并构建维度公式
        List<DimensionBo> dimensions = dimensionMapper.selectAllByScaleId(submitGaugeBo.getGaugeId())
                .stream().peek(DimensionBo::builderFormula).collect(Collectors.toList());
        // 根据维度名称统计数量
        Map<String, Long> collect = dimensions.stream().collect(Collectors.groupingBy(DimensionBo::getDimName, Collectors.counting()));
        // 初始化维度已处理的次数
        Map<String, Long> countByDimName = new HashMap<>();
        for (DimensionBo dimension : dimensions) {
            // 普通维度
            if (dimension.getDimType() == 1) {
                String dimName = dimension.getDimName();
                String ageRanger = dimension.getAgeRanger();
                countByDimName.put(dimName, countByDimName.getOrDefault(dimName, 0L) + 1);
                // 如果设置了多个相同维度名称，不到最后一个前性别条件不符合的直接过滤
                if (StringUtils.isNotBlank(dimension.getSuitSex()) && !dimension.getSuitSex().equals(submitGaugeBo.getFormData().get("sex")) && countByDimName.get(dimName) < collect.get(dimName)) {
                    continue;
                }
                // 如果设置了多个相同维度名称，不到最后一个前年龄条件不符合的直接过滤
                if (StringUtils.isNotBlank(ageRanger) && !(Boolean) AviatorUtil.compute(submitGaugeBo.getFormData(), ageRanger) && countByDimName.get(dimName) < collect.get(dimName)) {
                    continue;
                }
                String suitSex = dimension.getSuitSex();
                // 如果维度没有相同的，或者有相同维度名称走到最后一个，此时判断性别和年龄条件是否属于不匹配的
                dimension.setNonMatch((StringUtils.isNotBlank(suitSex) && !suitSex.equals(submitGaugeBo.getFormData().get("sex"))) || (StringUtils.isNotBlank(ageRanger) && !(Boolean) AviatorUtil.compute(submitGaugeBo.getFormData(), ageRanger)));
                // 计算维度所得分数
                dimension.computeDimScore(submitGaugeBo.getFormData());
                dimName = dimName
                        // 清除变量里面特殊字符串—，因为这个符号在维度名称里面可出现
                        .replaceAll("[—【】]", "");
                submitGaugeBo.getFormData().put(dimName + "原始分", dimension.getOriginalScore());
                submitGaugeBo.getFormData().put(dimName, dimension.getRealScore());
                // 如果维度性别和年龄条件都不匹配，则仅计算分数，不生成报告
                if (dimension.isNonMatch()) {
                    continue;
                }
                // 总评维度
            } else {
                dimension.computeDimScore(submitGaugeBo.getFormData());
            }
            // 生成维度报告
            generateRepContent(dimension, submitGaugeBo.getFormData(), getReportId.get());
        }
        // 原始答卷
        if (null != consumer) {
            consumer.accept(submitGaugeBo.getAnswers());
        }
        scheduledExecutorService.execute(()->{
            // 异步处理预警记录
            warnRecordService.generateWarnRecord(dimensions, submitGaugeBo.getPatient(), submitGaugeBo.getGaugeId(), getReportId.get());
        });
    }

    /**
     * <p>
     * 根据解释说明生成报告内容
     * </p>
     */
    private void generateRepContent(DimensionBo dimension, Map<String, Object> env, String reportId) {
        // 原始分
        BigDecimal originalScore = dimension.getOriginalScore();
        // 标准分
        BigDecimal standardScore = dimension.getRealScore();
//        // tips 判断的分数，如果计分方式为标准 --> 则取标准分，否则取原始分
        env.put("Score", standardScore);
        // 查询解释说明
        List<TblScaleDimNorm> explains = dimension.getDimNorms();
        // 过滤出符合解释说明的第一个条件
        Optional<TblScaleDimNorm> normExpDtoOptional = explains.stream()
                .filter(ex -> ((StringUtils.isBlank(ex.ageRanger()) || (Boolean) AviatorUtil.compute(env, ex.ageRanger()))
                        && (StringUtils.isBlank(ex.getSuitSex()) || ex.getSuitSex().equals(env.get("age")))
                        && (StringUtils.isBlank(ex.scoreExp()) || (Boolean) AviatorUtil.compute(env, ex.scoreExp()))))
                .findFirst();
        BigDecimal max = explains.stream().filter(f -> Objects.nonNull(f.getMaxScore())).max(Comparator.comparing(TblScaleDimNorm::getMaxScore)).map(TblScaleDimNorm::getMaxScore).orElse(null);
        // step 生成维度报告
        TblScaleReportContent reportContent = null;
        if (normExpDtoOptional.isPresent()) {
            TblScaleDimNorm tblScaleDimNorm = normExpDtoOptional.get();
            reportContent = TblScaleReportContent.builder()
                    .rId(reportId)// 报告编号
                    .dimId(dimension.getId())// 维度编号
                    .dimName(dimension.getDimName())// 维度名称
                    .exId(tblScaleDimNorm.getNormId())// 解释说明编号
                    .exStatus(tblScaleDimNorm.getNormName())// 状态
                    .originalScore(originalScore)// 原始分
                    .realScore(standardScore)// 标准分
                    .explanation(tblScaleDimNorm.getExplanation())// 描述
                    .recommend(tblScaleDimNorm.getRecommend()) // 建议
                    .highestScore(max)// 最高分
                    .factor(explains.isEmpty())// 是否仅作为因子分计算
                    .build();

        } else {
            reportContent = TblScaleReportContent.builder()
                    .rId(reportId)// 报告编号
                    .dimId(dimension.getId())// 维度编号
                    .dimName(dimension.getDimName())// 维度名称
                    .dimType(dimension.getDimType())
                    .exStatus("无")// 状态
                    .originalScore(dimension.isNonMatch() ? BigDecimal.ZERO : originalScore)// 原始分
                    .realScore(dimension.isNonMatch() ? BigDecimal.ZERO : standardScore)// 标准分
                    .explanation("无")// 描述
                    .recommend("无") // 建议
                    .highestScore(max)// 最高分
                    .factor(explains.isEmpty())// 是否仅作为因子分计算
                    .build();
        }
        reportContentMapper.insert(reportContent);
    }


}
